﻿using lctdevice;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace PCIe_M60_C_Sharp_Demo
{
    public partial class mainForm : Form
    {
        /// <summary>
        /// 构造
        /// </summary>
        public mainForm()
        {
            InitializeComponent();
        }

        #region 全局变量
        /// <summary>
        /// 从站资源
        /// </summary>
        short SlaveNum = 0;  //从站数量
        short AxisNum = 0;   //伺服数量 
        short IoSlaveNum = 0;//IO从站数
        short DiNum = 0;     //Di数量
        short DoNum = 0;     //Do数量
        short AiNum = 0;     //Ai数量
        short AoNum = 0;     //Ao数量
        int inVarNum;    //输入变量数
        int outVarNum;   //输出变量数
        
        /// <summary>
        /// 轴状态数组
        /// </summary>
        int ServoSts = 0;            //接收 伺服轴的运行状态：M_GetSts
        int DriverActualPos = 0;     //接收 驱动器内部的实际位置：M_ReadActualPosition
        int TargetPos = 0;           //接收 轴的目标位置：M_GetTargetPos
        double CmdPos = 0;           //接收 指令（规划）位置：M_GetCmd
        double CmdVel = 0;           //接收 指令（规划）速度：M_GetCmdVel
        double EncVel = 0;           //接收 轴的实际速度（即编码器的反馈速度）：M_GetEncVel
        double EncPos = 0;           //接收 轴的实际位置（即编码器的反馈位置）：M_GetEncPos

        /// <summary>
        /// 点位运动参数
        /// </summary>
        ecat_motion.CmdPrm CmdPrm = new ecat_motion.CmdPrm();//实例结构体“CmdPrm”，其中包括 acc 、dec 和 sTime 共3个字段
        int PositiveSoftLimit = 0;                           //正向软极限设置
        int NegativeSoftLimit = 0;                           //负向软极限设置
        uint InpBand = 0;                                    //运动完成判断范围
        uint InpTime = 0;                                    //运动完成停止时间
        double StopDec = 0;                                  //停止加速度
        double EmgDec = 0;                                   //急停加速度

        /// <summary>
        /// 插补运动（M_Line_All）
        /// </summary>
        int[] LineAllPosition = null;   //参与M_Line_All插补运动目标位置数组
        short[] LineAllAxisArray = null;//参与M_Line_All插补运动轴的轴号数组    

        /// <summary>
        /// Crd_1FIFO_0和Crd_2FIFO_0的连续插补设置
        /// </summary>
        short fifo_0 = 0;                                      
        short fifo_1 = 1;                                      //本范例只使用了编号为0的fifo寄存器，故“fifo_1”变量从未使用过
        ecat_motion.CrdCfg crdCfg_1 = new ecat_motion.CrdCfg();//实例1#坐标系的CrdCfg结构体（用于设置1#坐标系的连续插补参数）
        int Space_1 = 0;                                       //1#坐标系的0#FIFO剩余空间（Space_1 + CmdNum_1 = 512）
        short CmdNum_1 = 0;                                    //1#坐标系的0#FIFO里存在的点位数（即未到达的点位数量）
        short Sts_1 = 0;                                       //1#坐标系的0#FIFO的运行状态（1表示运动中，0表示停止状态）
        short crd_1 = 1;                                       //1#坐标系
        public static ecat_motion.CrdBlockData crdBlockData_1 = new ecat_motion.CrdBlockData();

        ecat_motion.CrdCfg crdCfg_2 = new ecat_motion.CrdCfg();//实例2#坐标系的CrdCfg结构体（用于设置2#坐标系的连续插补参数）
        int Space_2 = 0;                                       //2#坐标系的0#FIFO剩余空间（Space_2 + CmdNum_2 = 512）
        short CmdNum_2 = 0;                                    //2#坐标系的0#FIFO里存在的点位数（即未到达的点位数量）
        short Sts_2 = 0;                                       //2#坐标系的0#FIFO的运行状态：0表示停止（暂停）状态，1表示运动中，）
        short crd_2 = 2;                                       //2#坐标系
        double[] asv_2 = new double[UInt16.MaxValue * 128];    //2#坐标系速度前瞻运算时所使用的内存空间
        public static ecat_motion.CrdBlockData crdBlockData_2 = new ecat_motion.CrdBlockData();

        /// <summary>
        /// 其他变量
        /// </summary>
        short currentAxisID = 1;            //选中轴的轴号,默认是1
        short card = 0;                     //M60板卡的卡号（从0开始）
        short EmgFlag = 0;                  //急停信号
        short currentAxisOperatingMode = 0; //驱动器的运行模式
        short option = 0;//option为设置断线输出保持的参数，0-不保持；1-保持 
        #endregion

        #region 其他方法（对客户不重要）
        private void nudCurrentAxisID_ValueChanged(object sender, EventArgs e)// 选中轴的轴号 发生改变
        {
            currentAxisID = (short)nudCurrentAxisID.Value;
        }

        void AddLog(string txt, short ret)//添加log信息
        {
            if (ret == 0)
            {
                lstAddLog.Items.Add(txt + "成功,返回值：" + ret);
            }
            else
            {
                lstAddLog.Items.Add(txt + "失败,返回值：" + ret);
            }
            lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1;
        }

        void AddLog(string txt)//添加log信息
        {
            lstAddLog.Items.Add(txt);
            lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1;
        }
        void AddLog1(int data)//添加log信息
        {
            DI_lstLog.Items.Add(data);
            DI_lstLog.SelectedIndex = DI_lstLog.Items.Count - 1;
        }

        void AddLog2(int data)//添加log信息
        {
            DO_lstLog.Items.Add(data);
            DO_lstLog.SelectedIndex = DO_lstLog.Items.Count - 1;


        }

        void crcLog1(string txt,int data)//添加log信息
        {
            
            crc_box1.Items.Add(txt+data);
            crc_box1.SelectedIndex = crc_box1.Items.Count - 1;

        }
        void crcLog2(string txt, int data)//添加log信息
        {
            
            crc_box2.Items.Add(txt + data);
            crc_box2.SelectedIndex = crc_box2.Items.Count - 1;

        }
        #endregion

        /// <summary>
        /// 初始化PCIe-M60轴卡
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnInitialCard_Click(object sender, EventArgs e)
        {
            //setp1:打开轴卡
            short ret = ecat_motion.M_Open(card, 0);//打开轴卡,注意：第2个参数为预留参数,无效
            AddLog("打开轴卡" + card, ret);
            if (ret == 0)
            {
                UITimer.Enabled = true;

                #region setp2:设置急停相关的参数
                short senseLevel = 0;
                if (rdoEmgModeNC.Checked == true)
                    senseLevel = 1;//急停极性 → 1:常闭
                else
                    senseLevel = 0;//急停极性 → 0:常开
                ret = ecat_motion.M_SetEmgInv(senseLevel, card);//设置轴卡的急停模式
                AddLog("设置轴卡的急停模式", ret);
                if (ret != 0) { MessageBox.Show("ecat_motion.M_SetEmgInv 执行失败，返回值为：" + ret.ToString()); return; }

                ret = ecat_motion.M_SetEmgAction(0x00, card);//0x00：不减速，直接掉使能（默认值）
                AddLog("设置轴卡的急停动作", ret);
                if (ret != 0) { MessageBox.Show("ecat_motion.M_SetEmgAction 执行失败，返回值为：" + ret.ToString()); return; }

                ret = ecat_motion.M_ClrEmg(card);//复位急停状态
                AddLog("复位急停状态", ret);
                if (ret != 0) { MessageBox.Show("ecat_motion.M_ClrEmg 执行失败，返回值为：" + ret.ToString()); return; }
                #endregion

                #region setp3:加载eni文件
                //专家模式，建议用这种组态模式，这是新开发的模式，对从站的兼容性高、对用户使用的便捷性也好
                if (rdoExpertMode.Checked == true)
                {
                    ret = ecat_motion.M_LoadEni("C:\\Program Files (x86)\\LCT\\PCIe-M60\\Eni\\eni_expertmode_Card0.xml", card);//加载eni_expertmode_Card0.xml文件，该文件为驱动器、远程IO模块的通信参数配置文件
                    AddLog("加载eni_expertmode_Card0.xml", ret);
                }
                //常规模式，这是M60轴卡一直以来用的组态模式，后续不建议再使用这种模式了
                else
                {
                    ret = ecat_motion.M_LoadEni("C:\\Program Files (x86)\\LCT\\PCIe-M60\\Eni\\eni.xml", card);//加载eni.xml文件，该文件为驱动器、远程IO模块的通信参数配置文件
                    AddLog("加载eni.xml", ret);
                }
                #endregion

                #region 
                //如果使用快速连接
                if (rdoConnectFastMode.Checked == true)
                {
                    short isOp = -1;//从站是否都在OP状态？0：否 1：是
                    short isSlaveChange = -1;//从站拓扑是否发生改变？0：否 1：是
                    short isOffline = -1;//从站是否有掉线？0：否 1：是
                    ret = ecat_motion.M_GetConnectStatus(ref isOp, ref isSlaveChange, ref isOffline, card);
                    if (ret == 0)
                    {
                        if (isOp == 0 || isSlaveChange == 1 || isOffline == 1)
                        {
                            MessageBox.Show("当前状态不满足使用快速连接功能，将自动进行常规连接操作！！！");
                            ret = ecat_motion.M_ResetFpga(card);
                            AddLog("卡" + card + "M_ResetFpga", ret);//重置FPGA芯片
                            if (ret != 0) { MessageBox.Show("ecat_motion.M_ResetFpga 执行失败，返回值为：" + ret.ToString()); return; }
                            Thread.Sleep(500); //延时500ms，该延时必须有
                        }
                    }
                    else
                    {
                        MessageBox.Show("ecat_motion.M_GetConnectStatus 执行失败，返回值为：" + ret.ToString());
                        return;
                    }
                }
                //如果使用常规连接
                else
                {
                    ret = ecat_motion.M_ResetFpga(card);
                    AddLog("卡" + card + "M_ResetFpga", ret);//重置FPGA芯片
                    if (ret != 0) { MessageBox.Show("ecat_motion.M_ResetFpga 执行失败，返回值为：" + ret.ToString()); return; }
                    Thread.Sleep(500); //延时500ms，该延时必须有
                }

                AddLog("卡" + card +"连接总线中......请勿操作！");
                ret = ecat_motion.M_ConnectECAT(option, card);//连接总线，说明：常规连接需要一定的时间，所需时间会随着从站数量的增多而变长；快速连接会很快）
                AddLog("卡" + card + "连接总线", ret);
                if (ret == 0)
                {
                    Thread.Sleep(500);//延时500ms，该延时必须有
                    ret = ecat_motion.M_LoadParamFromFile("C:\\Program Files (x86)\\LCT\\PCIe-M60\\Motion_Assistant\\AxisParam\\ParamCard0.ini", card);
                    AddLog("加载ParamCard0.ini轴参数文件", ret);//从文件加载参数
                    if (ret == 0)
                        MessageBox.Show("初始化轴卡成功！");

                    //获取从站资源，检查实际资源（从站数量、轴数量、IO模块的数量、DI、DO、AI和AO）和线上资源是否一致
                    ecat_motion.SL_RES Sl_res = new ecat_motion.SL_RES();
                    ret = ecat_motion.M_GetSlaveResource(out Sl_res, card);//获取Ethercat网络中的从站资源
                    if (ret == 0)//如果返回值为0，即获取从站资源成功
                    {
                        SlaveNum = (short)Sl_res.SlaveNum;//变量 SlaveNum 接收从站数量
                        labSlaveNum.Text = "从站数量:" + SlaveNum.ToString();
                        AxisNum = (short)Sl_res.AxisNum;//变量 ServoNum 接收轴数量
                        labAxisNum.Text = "轴数量:" + AxisNum.ToString();
                        IoSlaveNum = (short)Sl_res.IoSlaveNum;//变量 IoSlaveNum 接收 IO从站数量
                        labIOSlaveNum.Text = "IO从站数：" + IoSlaveNum.ToString();
                        DiNum = (short)Sl_res.DiNum;//变量 DiNum 接收 DI数量
                        labDiNum.Text = "Di数量:" + DiNum.ToString();
                        DoNum = (short)Sl_res.DoNum;//变量 DoNum 接收 DO数量
                        labDoNum.Text = "Do数量:" + DoNum.ToString();
                        AiNum = (short)Sl_res.AiNum;//变量 AiNum 接收 AI数量
                        labAiNum.Text = "Ai数量:" + AiNum.ToString();
                        AoNum = (short)Sl_res.AoNum;//变量 AoNum 接收 AO数量
                        labAoNum.Text = "Ao数量:" + AoNum.ToString();
                        inVarNum = (int)Sl_res.inVarNum;//变量 inVarNum 接收 inVar数量
                        labInVarNum.Text = "输入变量数:" + inVarNum.ToString();
                        outVarNum = (int)Sl_res.outVarNum;//变量 outVarNum 接收 outVar数量
                        labOutVarNum.Text = "输出变量数:" + outVarNum.ToString();

                        nudCurrentAxisID.Maximum = AxisNum;
                        btnInitialCard.BackColor = Color.Green;
                        btnInitialCard.Text = "初始化轴卡_成功！";
                        btnInitialCard.Enabled = false;
                        btnCloseCard.BackColor = Color.White;
                        btnCloseCard.Enabled = true;
                    }
                    else
                        MessageBox.Show("卡" + card + "获取从站资源失败！");
                }
                else
                {
                    MessageBox.Show("ecat_motion.M_ConnectECAT 执行失败，返回值为：" + ret.ToString());
                    return;
                }
                #endregion
            }
        }

        /// <summary>
        /// 关闭PCIe-M60轴卡
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCloseCard_Click(object sender, EventArgs e)
        {
            try//尝试使所有的轴去使能
            {
                for (short i = 1; i < AxisNum + 1; i++)
                    ecat_motion.M_Servo_Off(i, 0);
                Thread.Sleep(100);
            }
            catch { }

            try//尝试断开EtherCAT总线
            {
                short ret1 = ecat_motion.M_DisconnectECAT(card);//断开总线段
            }
            catch { }

            short ret2 = ecat_motion.M_Close(card);
            AddLog("关闭轴卡" + card, ret2);
            if (ret2 == 0)
            {
                btnInitialCard.Text = "初始化轴卡";
                btnCloseCard.Enabled = false;
                btnInitialCard.Enabled = true;
                btnInitialCard.BackColor = Color.White;
                btnCloseCard.BackColor = Color.Green;
                UITimer.Enabled = false;
            }
        }

        /// <summary>
        /// 连接EtherCAT总线 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnConnect_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_ResetFpga(card);//重置FPGA芯片
            if (ret != 0)
            {
                AddLog("重置FPGA芯片",ret);
                return;
            }
            Thread.Sleep(500);//再延时500ms（该延时必须有）

            AddLog("连接总线中......请勿操作！");
            short option = 0;//option为设置断线输出保持的参数，0-不保持；1-保持 
            ret = ecat_motion.M_ConnectECAT(option, card);//连接总线（注：连接总线需要一定的时间，所需时间会随着从站数量的增多而变长）
            AddLog("连接总线", ret);
            if (ret == 0)
            {
                //获取从站资源
                ecat_motion.SL_RES Sl_res = new ecat_motion.SL_RES();  //实例ecat_motion.cs接口中用到的“SL_RES 结构体”
                ret = ecat_motion.M_GetSlaveResource(out Sl_res, card);//获取Ethercat网络中的从站资源
                if (ret == 0)//如果返回值为0，即获取从站资源成功
                {
                    SlaveNum = (short)Sl_res.SlaveNum;//变量 SlaveNum 接收从站数量
                    labSlaveNum.Text = "从站数量:" + SlaveNum.ToString();
                    AxisNum = (short)Sl_res.AxisNum;//变量 ServoNum 接收轴数量
                    labAxisNum.Text = "轴数量:" + AxisNum.ToString();
                    DiNum = (short)Sl_res.DiNum;//变量 DiNum 接收 DI数量
                    labDiNum.Text = "Di数量:" + DiNum.ToString();
                    DoNum = (short)Sl_res.DoNum;//变量 DoNum 接收 DO数量
                    labDoNum.Text = "Do数量:" + DoNum.ToString();
                    AiNum = (short)Sl_res.AiNum;//变量 AiNum 接收 AI数量
                    labAiNum.Text = "Ai数量:" + AiNum.ToString();
                    AoNum = (short)Sl_res.AoNum;//变量 AoNum 接收 AO数量
                    labAoNum.Text = "Ao数量:" + AoNum.ToString();
                    IoSlaveNum = (short)Sl_res.IoSlaveNum;//变量 IoSlaveNum 接收 IO从站数量
                    labIOSlaveNum.Text = "IO从站数：" + IoSlaveNum.ToString();

                    if (AxisNum > 0)
                    {
                        nudCurrentAxisID.Maximum = AxisNum;     // nudCurrentAxisID 控件可选的最大轴号为从站资源中轴的数量
                        nudCurrentAxisID.Minimum = 1;           // nudCurrentAxisID 控件可选的最小轴号为1
                    }
                } 
            }
        }

        /// <summary>
        /// 轴号 使能
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCurrentAxisServoOn_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_Servo_On(currentAxisID, card);
            AddLog("轴" + currentAxisID + "M_Servo_On", ret);
        }

        /// <summary>
        /// 下使能
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCurrentAxisServoOff_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_Servo_Off(currentAxisID, card);
            AddLog("轴" + currentAxisID + "去使能", ret);
        }

        /// <summary>
        /// UI
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UITimer_Tick(object sender, EventArgs e)
        {                  
            #region UI显示轴卡的EMG急停信号状态
            ecat_motion.M_GetEmg(ref EmgFlag, card);//轴卡EMG信号状态: EmgFlag == 1表示急停中（保持信号），EmgFlag == 0 表示急停动作未触发。
            if (EmgFlag == 1)
            {
                btnRestEmgStatus.Enabled = true;
                btnEmgStatus.BackColor = Color.Red;
                btnEmgStatus.Text = "急停状态";
            }
            else
            {
                btnRestEmgStatus.Enabled = false;
                btnEmgStatus.BackColor = Color.Green;
                btnEmgStatus.Text = "非急停状态";
            }
            #endregion

            #region UI显示当前轴的状态
            if (AxisNum > 0)//如果从站中存在轴
            {
                //*****UI显示当前轴的指令位置、指令速度 、 编码器位置、 编码器速度、驱动器实际位置和目标位置*****//⬇   

                ecat_motion.M_GetCmd(currentAxisID, out CmdPos, 1, card);//获取轴规划位置
                txtCommandPostion.Text = CmdPos.ToString("f0");

                ecat_motion.M_GetCmdVel(currentAxisID, out CmdVel, 1, card);//获取轴规划速度
                txtCommandVelocity.Text = CmdVel.ToString("f0");
                
                ecat_motion.M_GetEncPos(currentAxisID, out EncPos, 1, card);//获取编码器位置
                txtEncoderPostion.Text = EncPos.ToString("f0");
                
                ecat_motion.M_GetEncVel(currentAxisID, out EncVel, 1, card);//获取编码器速度
                txtEncoderVelocity.Text = EncVel.ToString("f0");
                
                ecat_motion.M_ReadActualPosition(currentAxisID, out DriverActualPos, 1, card);//获取驱动器内部实际位置
                txtDriveActualPostion.Text = DriverActualPos.ToString("f0");
                
                ecat_motion.M_GetTargetPos(currentAxisID, ref TargetPos, card);//获取轴目标位置
                txtTargetPostion.Text = TargetPos.ToString("f0");
                //************************************************************************************************//⬆


                //************************ UI显示当前轴的状态 ******************************//⬇              
                //轴的状态字每一个Bit位代表的含义请参考PCIe-M60函数手册里的“M_GetSts()”方法
                ecat_motion.M_GetSts(currentAxisID, out ServoSts, 1, card);//获取轴状态

                //1 伺服报警 alarm
                if ((ServoSts & 0x02) == 0x02)
                    labAlarm.BackColor = Color.Red;
                else
                    labAlarm.BackColor = Color.Black;

                //2 伺服使能 servo on
                if ((ServoSts & 0x200) == 0x200)
                    labServoOn.BackColor = Color.Red;
                else
                    labServoOn.BackColor = Color.Black;

                //3 正极限 postive limit
                if ((ServoSts & 0x20) == 0x20)
                    labPositiveLimit.BackColor = Color.Red;
                else
                    labPositiveLimit.BackColor = Color.Black;

                //4 原点信号 origin
                if ((ServoSts & 0x100000) == 0x100000)
                    labOrigin.BackColor = Color.Red;
                else
                    labOrigin.BackColor = Color.Black;

                //5 负极限 negative limit
                if ((ServoSts & 0x40) == 0x40)
                    labNegativeLimit.BackColor = Color.Red;
                else
                    labNegativeLimit.BackColor = Color.Black;

                //6 运动 motion
                if ((ServoSts & 0x400) == 0x400)
                    labMotion.BackColor = Color.Red;
                else
                    labMotion.BackColor = Color.Black;

                //7 轴到位 inp
                if ((ServoSts & 0x800) == 0x800)
                    labINP.BackColor = Color.Red;
                else
                    labINP.BackColor = Color.Black;                

                //8 掉线 offline
                if ((ServoSts & 0x1000000) == 0x1000000)
                    labOffline.BackColor = Color.Red;
                else
                    labOffline.BackColor = Color.Black;

                //9 缓停触发 SSTP
                if ((ServoSts & 0x80) == 0x80)
                    labSSTP.BackColor = Color.Red;
                else
                    labSSTP.BackColor = Color.Black;
                //**************************************************************************//⬆
            }           
            #endregion

            #region UI显示“1#坐标系0#FIFO”和“2#坐标系0#FIFO”的运行状态
            if (tabControl1.SelectedTab.Text == "连续插补")
            {
                ecat_motion.M_CrdStatus(crd_1, out Sts_1, out CmdNum_1, out Space_1, fifo_0, card);//获取“1#坐标系0#FIFO”的运行状态
                txtCrd_1FIFO_0CmdNum.Text = CmdNum_1.ToString();
                txtCrd_1FIFO_0Space.Text = Space_1.ToString();
                txtCrd_1FIFO_0Sts.Text = Sts_1.ToString();

                ecat_motion.M_CrdStatus(crd_2, out Sts_2, out CmdNum_2, out Space_2, fifo_0, card);//获取“2#坐标系0#FIFO”的运行状态
                txtCrd_2FIFO_0CmdNum.Text = CmdNum_2.ToString();
                txtCrd_2FIFO_0Space.Text = Space_2.ToString();
                txtCrd_2FIFO_0Sts.Text = Sts_2.ToString();
            }
            #endregion

            //num_datanum_input.Value = dataNum;
        }

        /// <summary>
        /// 清除急停信号
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnRestEmgStatus_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_ClrEmg(card);
            AddLog("清除急停信号指令执行", ret);
        }

        private void btnClearCurrentAxisErrorStatusAndDriveAlarm_Click(object sender, EventArgs e)//清除当前轴的错误轴状态和驱动器报警
        {
            short ret = ecat_motion.M_ClrSts(currentAxisID, 1, card);//第2个参数为将要执行的轴的数量
            AddLog("清除当前轴的错误轴状态和驱动器报警", ret);
        }

        /// <summary>
        /// 设置轴卡急停的接线方式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSetCardEmgInv_Click(object sender, EventArgs e)
        {
            switch (cmbEmgInv.SelectedIndex)
            {
                case 0:
                    short ret = ecat_motion.M_SetEmgInv(1, card);
                    AddLog("设置轴卡急停的接线方式为常闭接法", ret);
                    break;
                case 1:
                    ret = ecat_motion.M_SetEmgInv(0, card);
                    AddLog("设置轴卡急停的接线方式为常开接法", ret);
                    break;
            }            
        }

        /// <summary>
        /// 设置轴卡的急停动作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSetCardEmgAction_Click(object sender, EventArgs e)//
        {
            switch ((short)cmbCardEmgAction.SelectedIndex)
            {
                case 0:
                    ecat_motion.M_SetEmgAction(0x00, card);//不减速，直接掉使能（默认值）
                    break;
                case 1:
                    ecat_motion.M_SetEmgAction(0x01, card);//以缓停减速度停机，然后掉使能
                    break;
                case 2:
                    ecat_motion.M_SetEmgAction(0x02, card);//以急停减速度停机，然后掉使能
                    break;
                case 3:
                    ecat_motion.M_SetEmgAction(0x11, card);//以缓停减速度停机，不掉使能
                    break;
                case 4:
                    ecat_motion.M_SetEmgAction(0x12, card);//以急停减速度停机，不掉使能
                    break;
            }
        }

        /// <summary>
        /// 绝对运动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartAbsoluteMotion_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_AbsMove(currentAxisID, (int)nudAbsolutePosition.Value, (double)nudAbsoluteMotionVelocity.Value, card);
            AddLog("轴" + currentAxisID + "开始绝对运动", ret);
            if (ret == 0)
            {
                Task task = new Task(() =>
                {
                    int currentAxisStatus = 0;
                    do
                    {
                        Thread.Sleep(100);
                        ecat_motion.M_GetSts((short)currentAxisID, out currentAxisStatus, 1, card);
                    }
                    //while ((currentAxisStatus & 0x400) == 0x400 || (currentAxisStatus & 0x800) == 0);//推荐使用“同时判断motion和inp信号”的方法，当motion信号为0和inp信号为1时即认为运动完成（运动到位）。但使用此判断条件为：该轴的运动参数Band和Stime都必须赋非0值。
                    //或者⬇
                    while ((currentAxisStatus & 0x400) == 0x400);//（也可以）只判断motion信号，当motion信号为0时即认为运动完成

                    Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "绝对运动完成"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; };
                    lstAddLog.Invoke(action);
                });
                task.Start();
            }        
        }

        /// <summary>
        /// 单轴停止运动(平滑)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCurrentAxisStop_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_StopSingleAxis(currentAxisID, 0, card);//option为停止运动的方式: 0 表示采用平滑减速度停止，1 表示采用急停减速度停止
            AddLog("轴" + currentAxisID + "停止运动(平滑)", ret);
        }

        /// <summary>
        /// 单轴停止运动(急停)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCurrentAxisEmgStop_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_StopSingleAxis(currentAxisID, 1, card);//option为停止运动的方式: 0 表示采用平滑减速度停止，1 表示采用急停减速度停止
            AddLog("轴" + currentAxisID + "停止运动(急停)", ret);
        }

        /// <summary>
        /// 相对运动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartRelativeMotion_Click(object sender, EventArgs e)
        {
            short Ret = ecat_motion.M_RelMove(currentAxisID, (int)nudRelativeDistance.Value, (double)nudRelativeMotionVelocity.Value, card);
            AddLog("轴" + currentAxisID + "开始相对运动", Ret);
            if (Ret == 0)
            {
                Task task = new Task(() =>
                {
                    int currentAxisStatus = 0;
                    do//因为执行机构存在滞后性，所以判断运动完成建议使用 do while 结构
                    {
                        Thread.Sleep(100);
                        ecat_motion.M_GetSts((short)currentAxisID, out currentAxisStatus, 1, card);
                    }
                    //while ((currentAxisStatus & 0x400) == 0x400 || (currentAxisStatus & 0x800) == 0);//推荐使用“同时判断motion和inp信号”的方法，当motion信号为0和inp信号为1时即认为运动完成。但使用此判断条件为：该轴的运动参数Band和Stime都必须赋非0值。
                    //或者⬇                    
                    while ((currentAxisStatus & 0x400) == 0x400);//（也可以）只判断motion信号，当motion信号为0时即认为运动完成

                    Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "相对运动完成"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; };
                    lstAddLog.Invoke(action);
                });
                task.Start();
            }        
        }

        /// <summary>
        /// 开始Jog+运动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPostiveJogMotion_MouseDown(object sender, MouseEventArgs e)
        {
            short ret = ecat_motion.M_Jog(currentAxisID, (double)nudJogMotionVelocity.Value, card);
            AddLog("轴" + currentAxisID + "开始Jog+运动", ret);
        }

        /// <summary>
        /// 停止Jog+运动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnPostiveJogMotion_MouseUp(object sender, MouseEventArgs e)
        {
            short ret = ecat_motion.M_Jog(currentAxisID, 0, card);
            AddLog("轴" + currentAxisID + "停止Jog+运动", ret);
        }

        /// <summary>
        /// 开始Jog-运动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnNegativeJogMotion_MouseDown(object sender, MouseEventArgs e)
        {
            short ret = ecat_motion.M_Jog(currentAxisID, (double)nudJogMotionVelocity.Value * -1, card);
            AddLog("轴" + currentAxisID + "开始Jog-运动", ret);
        }

        /// <summary>
        /// 停止Jog-运动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnNegativeJogMotion_MouseUp(object sender, MouseEventArgs e)
        {
            short ret = ecat_motion.M_Jog(currentAxisID, 0, card);
            AddLog("轴" + currentAxisID + "停止Jog-运动", ret);
        }

        /// <summary>
        /// 获取当前轴的正负软极限参数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGetSoftLimit_Click(object sender, EventArgs e)
        {
            //如果正负软极限的值都为0，表示正负软极限功能为关闭状态
            short ret = ecat_motion.M_GetSoftLimit(currentAxisID, out PositiveSoftLimit, out NegativeSoftLimit, card);
            AddLog("获取当前轴的正负软极限", ret);
            if (ret == 0)
            {               
                nudPositiveSoftLimit.Value = PositiveSoftLimit;//负向软极限  
                nudNegativeSoftLimit.Value = NegativeSoftLimit;//正向软极限 
            }
        }

        /// <summary>
        /// 设置当前轴的正负软极限参数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSetSoftLimit_Click(object sender, EventArgs e)
        {
            //如果正负软极限的值都给0，表示不启用正负软极限功能
            PositiveSoftLimit = (int)nudPositiveSoftLimit.Value;//正向软极限
            NegativeSoftLimit = (int)nudNegativeSoftLimit.Value;//负向软极限         
            short ret = ecat_motion.M_SetSoftLimit(currentAxisID, PositiveSoftLimit, NegativeSoftLimit, card);
            AddLog("设置当前轴的正负软极限", ret);
        }

        /// <summary>
        /// 获取当前轴的INP到位参数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGetInpBandAndTime_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_GetAxisBand(currentAxisID, out InpBand, out InpTime, card);
            AddLog("获取当前轴的INP到位参数", ret);
            if (ret == 0)
            {
                nudInpBand.Value = InpBand;//到位范围（±InpBand/2），单位：脉冲
                nudInpTime.Value = InpTime;//到位稳定时间，单位：ms
            }            
        }

        /// <summary>
        /// 设置INP到位判断，设置以后INP有效
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSetInpBandAndTime_Click(object sender, EventArgs e)
        {
            InpBand = (uint)nudInpBand.Value;//到位范围（±InpBand/2），单位：脉冲
            InpTime = (uint)nudInpTime.Value;//到位稳定时间，单位：ms
            short ret = ecat_motion.M_SetAxisBand(currentAxisID, InpBand, InpTime, card);
            AddLog("设置当前轴的INP到位参数", ret);
        }

        private void btnGetSingAxisAccAndDec_Click(object sender, EventArgs e)//获取单轴加速度、减速度和加加速度时间（sf）
        {                        
            short ret = ecat_motion.M_GetMove(currentAxisID, out CmdPrm, card);
            AddLog("获取当前轴的INP到位参数", ret);
            if (ret == 0)
            {
                nudSingAxisAcc.Value = Convert.ToDecimal(CmdPrm.acc); //单轴加速度
                nudSingAxisDec.Value = Convert.ToDecimal(CmdPrm.dec); //单轴减速度
                nudSf.Value = Convert.ToDecimal(CmdPrm.sTime);        //单轴加加速时间
            }
        }

        private void btnSetSingAxisAccAndDec_Click(object sender, EventArgs e)//设置单轴加速度、减速度和加加速度时间（sf）
        {
            CmdPrm.acc = (double)nudSingAxisAcc.Value;//单轴加速度
            CmdPrm.dec = (double)nudSingAxisDec.Value;//单轴减速度
            CmdPrm.sTime = (short)nudSf.Value;        //单轴加加速时间
            short ret = ecat_motion.M_SetMove(currentAxisID, ref CmdPrm, card);
            AddLog("设置当前轴的INP到位参数", ret);
        }

        private void btnGetStopDecAndEmgDec_Click(object sender, EventArgs e)//获取单轴停止减速度和急停减速度
        {
            short ret = ecat_motion.M_GetStopDec(currentAxisID, out StopDec, out EmgDec, card);
            
            AddLog("获取单轴停止减速度和急停减速度", ret);
            if (ret == 0)
            {
                nudStopDec.Value = (decimal)StopDec;//停止（平滑）减速度
                nudEmgDec.Value = (decimal)EmgDec;  //急停减速度
            }            
        }

        private void btnSetStopDecAndEmgDec_Click(object sender, EventArgs e)
        {
            double StopDec = (double)nudStopDec.Value;//停止（平滑）减速度
            double EmgDec = (double)nudEmgDec.Value;  //急停减速度
            short ret = ecat_motion.M_SetStopDec(currentAxisID, StopDec, EmgDec, card);
            AddLog("设置单轴停止减速度和急停减速度", ret);
        }

        private void btnGetLockOrUnlock_Click(object sender, EventArgs e)
        {
            short on = 0;
            short ret = ecat_motion.M_GetAxisStsSync(currentAxisID, ref on, card);
            AddLog("获取限位信号是否锁定", ret);
            nudLockOrUnlock.Value = on;
        }

        private void btnSetLockOrUnlock_Click(object sender, EventArgs e)
        {
            short on = (short)nudLockOrUnlock.Value;
            short ret = ecat_motion.M_SetAxisStsSync(currentAxisID, on, card);
           
            AddLog("设置限位信号是否锁定", ret);
        }
        

        private void btnGetAllCurrentAxisparameters_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_GetSoftLimit(currentAxisID, out PositiveSoftLimit, out NegativeSoftLimit, card);//如果正负软极限的值都为0，表示正负软极限功能为关闭状态
            if (ret == 0)
            {
                nudPositiveSoftLimit.Value = PositiveSoftLimit;//负向软极限  
                nudNegativeSoftLimit.Value = NegativeSoftLimit;//正向软极限 
            }

            ret += ecat_motion.M_GetAxisBand(currentAxisID, out InpBand, out InpTime, card);
            if (ret == 0)
            {
                nudInpBand.Value = InpBand;//到位范围（±InpBand/2），单位：脉冲
                nudInpTime.Value = InpTime;//到位稳定时间，单位：ms
            }

            ret += ecat_motion.M_GetMove(currentAxisID, out CmdPrm, card);
            if (ret == 0)
            {
                nudSingAxisAcc.Value = Convert.ToDecimal(CmdPrm.acc); //单轴加速度
                nudSingAxisDec.Value = Convert.ToDecimal(CmdPrm.dec); //单轴减速度
                nudSf.Value = Convert.ToDecimal(CmdPrm.sTime);        //单轴加加速时间
            }

            ret += ecat_motion.M_GetStopDec(currentAxisID, out StopDec, out EmgDec, card);
            if (ret == 0)
            {
                nudStopDec.Value = (decimal)StopDec;//停止（平滑）减速度
                nudEmgDec.Value = (decimal)EmgDec;  //急停减速度
            }

            AddLog("一键获取当前轴所有的运动参数", ret);
        }

        private void btnSetAllCurrentAxisparameters_Click(object sender, EventArgs e)
        {            
            PositiveSoftLimit = (int)nudPositiveSoftLimit.Value;//正向软极限
            NegativeSoftLimit = (int)nudNegativeSoftLimit.Value;//负向软极限         
            short ret = ecat_motion.M_SetSoftLimit(currentAxisID, PositiveSoftLimit, NegativeSoftLimit, card);//如果正负软极限的值都给0，表示不启用正负软极限功能

            InpBand = (uint)nudInpBand.Value;//到位范围（±InpBand/2），单位：脉冲
            InpTime = (uint)nudInpTime.Value;//到位稳定时间，单位：ms
            ret += ecat_motion.M_SetAxisBand(currentAxisID, InpBand, InpTime, card);

            CmdPrm.acc = (double)nudSingAxisAcc.Value;//单轴加速度
            CmdPrm.dec = (double)nudSingAxisDec.Value;//单轴减速度
            CmdPrm.sTime = (short)nudSf.Value;        //单轴加加速时间
            ret += ecat_motion.M_SetMove(currentAxisID, ref CmdPrm, card);

            double StopDec = (double)nudStopDec.Value;//停止（平滑）减速度
            double EmgDec = (double)nudEmgDec.Value;  //急停减速度
            ret += ecat_motion.M_SetStopDec(currentAxisID, StopDec, EmgDec, card);

            short on = (short)nudLockOrUnlock.Value;
            ret += ecat_motion.M_SetAxisStsSync(currentAxisID, on, card);
            AddLog("一键设置当前轴所有的运动参数", ret);
        }

        private void btnGetCurrentAxisHomeParameters_Click(object sender, EventArgs e)//获取当前轴的回零参数
        {
            short HomeMode = 0;      //回零模式
            int HomeOffSet = 0;      //回零偏移
            uint HomeSpeed1 = 0;     //第一回零速度
            uint HomeSpeed2 = 0;     //第二回零速度
            uint HomeAcc = 0;        //回零加速度
            ushort ProbeFunction = 0;//预留，默认为 0
            short ret = ecat_motion.M_GetHomingPrm(currentAxisID, ref HomeMode, ref HomeOffSet, ref HomeSpeed1, ref HomeSpeed2, ref HomeAcc, ref ProbeFunction, card);
            AddLog("获取当前轴的回零参数", ret);
            if (ret == 0)
            {
                nudHomeMode.Value =  HomeMode;
                nudHomeOffset.Value = HomeOffSet;
                nudHomeSpeed1.Value = HomeSpeed1;
                nudHomeSpeed2.Value = HomeSpeed2;
                nudHomeAcc.Value = HomeAcc;
            }
        }

        private void btnSetCurrentAxisHomeParameters_Click(object sender, EventArgs e)//设置当前轴的回零参数
        {
            short HomeMode = (short)nudHomeMode.Value;  //回零模式
            int HomeOffSet = (short)nudHomeOffset.Value;//回零偏移
            uint HomeSpeed1 = (uint)nudHomeSpeed1.Value;//第一回零速度
            uint HomeSpeed2 = (uint)nudHomeSpeed2.Value;//第二回零速度
            uint HomeAcc = (uint)nudHomeAcc.Value;      //回零加速度
            ushort ProbeFunction = 0;                   //预留，默认为 0
            short ret = ecat_motion.M_SetHomingPrm(currentAxisID, HomeMode, HomeOffSet, HomeSpeed1, HomeSpeed2, HomeAcc, ProbeFunction, card);
            AddLog("设置当前轴的回零参数", ret);
        }

        /// <summary>
        /// 回零2 超时保护
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartHoming_2_Click(object sender, EventArgs e)
        {
            int HomeTimeOut = (int)nudHomeTimeOut.Value;
            short mode = 6;//6对应驱动器的回零工作模式(Homing)
            short ret = ecat_motion.M_SetHomingMode( currentAxisID, mode, card);//设置驱动器的工作模式
            Thread.Sleep(50);//此延时有必要
            if (ret == 0)
            {
                ret = ecat_motion.M_HomingStart(currentAxisID, card);//开始回零运动
                if (ret == 0)
                {
                    Task task = new Task(() =>
                    {
                        ret = ecat_motion.M_WaitHomingFinished(currentAxisID, HomeTimeOut, card);
                        if (ret == 0)
                        {
                            //判断 move信号 和 HomingError信号
                            double Cmd = 0;
                            int currentAxisStatus = 0;
                            ecat_motion.M_GetSts(currentAxisID, out currentAxisStatus, 1, card);
                            short Ret = ecat_motion.M_GetCmd(currentAxisID, out Cmd, 1, card);//获取轴规划位置
                            if (Ret == 0)
                            {
                                short HomeMode = 0;      //回零模式
                                int HomeOffSet = 0;      //回零偏移
                                uint HomeSpeed1 = 0;     //第一回零速度
                                uint HomeSpeed2 = 0;     //第二回零速度
                                uint HomeAcc = 0;        //回零加速度
                                ushort ProbeFunction = 0;//预留，默认为 0
                                ret = ecat_motion.M_GetHomingPrm(currentAxisID, ref HomeMode, ref HomeOffSet, ref HomeSpeed1, ref HomeSpeed2, ref HomeAcc, ref ProbeFunction, card);
                                if (ret == 0)
                                {
                                    if (((currentAxisStatus & 0x10000) != 0x10000) && ((currentAxisStatus & 0x400) != 0x400) && (Math.Abs(Cmd - HomeOffSet) < 50))//50表示50个脉冲，经验值。如果(轴没有给出HomingError信号)&&(轴不再运动)&&(当前位置在0附近)
                                    {
                                        Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "回零_成功"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; }; lstAddLog.Invoke(action);
                                        MessageBox.Show("轴" + currentAxisID + "回零_成功");
                                    }
                                    else
                                    {
                                        Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "回零异常"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; }; lstAddLog.Invoke(action);
                                        MessageBox.Show("轴" + currentAxisID + "回零异常");
                                    }
                                }
                                else
                                {
                                    MessageBox.Show("轴" + currentAxisID + "获取回零参数_失败！\n回零异常");
                                }
                            }
                            else
                            {
                                Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "回零异常"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; }; lstAddLog.Invoke(action);
                                MessageBox.Show("ecat_motion.M_GetCmd ret == " + Ret + "\n轴" + currentAxisID + "回零异常");
                            }
                        }
                        else if (ret == 25)//回零超时
                        {
                            Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "回零超时，请提高回零速度 或者 将回零超时时间调大"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; }; lstAddLog.Invoke(action);
                            MessageBox.Show("轴" + currentAxisID + "回零超时，请提高回零速度 或者 将回零超时时间调大");
                        }
                    });
                    task.Start();
                }
                else
                {
                    MessageBox.Show("轴" + currentAxisID + "开始回零_失败");
                }
            }
            else
            {
                MessageBox.Show("将轴"+ currentAxisID + "设置为回零模式_失败");
            }
        }

        private void btnStopHoming_Click(object sender, EventArgs e)
        {
            short ret = 0;
            try
            {
                ret = ecat_motion.M_HomeCancelSingleAxis(currentAxisID, card);
            }
            catch
            {
                //ret = ecat_motion.M_HomeCancel(Convert.ToUInt32((long)1 << (short)(currentAxisID - 1)), card);
            }

            int count = 0;
            do
            {
                Thread.Sleep(500);
                ret = ecat_motion.M_SetHomingMode(currentAxisID, 8, card);
                count++;
            } while (ret != 0 && count < 10);

            AddLog("轴" + currentAxisID + "停止回零", ret);
        }

        #region 插补运动( M_LineAll )
        private void btnStartLineAll_Click(object sender, EventArgs e)//开始插补运动
        {
            short dimension = (short)nudLineAllDimension.Value;                //插补维度（参与插补运动轴的数量，最小为2，最大为6）
            LineAllAxisArray = new short[dimension];                           //声明参与插补运动的轴数组,其长度等于参与插补运动轴的数量
            LineAllPosition = new int[dimension];                              //声明轴目标位置数组，其长度等于参与LineAll插补运动轴的数量，并且其每个元素和LineAllAxisArray[]里的每个元素相对应
            double acc = (double)nudLineAllAcc.Value;                          //参与M_Line_All插补运动轴的加速度
            double vel = (double)nudLineAllSingAxisMaxVelocity.Value;          //参与M_Line_All插补运动轴的最大速度    

            LineAllAxisArray[0] = (short)nudAxis_a.Value;                      //A轴对应的轴号
            LineAllPosition[0] = (int)nudAxis_a_Postion.Value;                 //A轴对应轴号的目标位置
            LineAllAxisArray[1] = (short)nudAxis_b.Value;                      //B轴对应的轴号
            LineAllPosition[1] = (int)nudAxis_b_Postion.Value;                 //B轴对应轴号的目标位置
            if (dimension >= 3)//如果插补维度>=3
            {
                LineAllAxisArray[2] = (short)nudAxis_c.Value;                  //C轴对应的轴号
                LineAllPosition[2] = (int)nudAxis_c_Postion.Value;             //C轴对应轴号的目标位置
                if (dimension >= 4)//如果插补维度>=4
                {
                    LineAllAxisArray[3] = (short)nudAxis_d.Value;              //获取D轴对应的轴号
                    LineAllPosition[3] = (int)nudAxis_d_Postion.Value;         //获取D轴对应轴号的目标位置
                    if (dimension >= 5)//如果插补维度>=5
                    {
                        LineAllAxisArray[4] = (short)nudAxis_e.Value;          //获取E轴对应的轴号
                        LineAllPosition[4] = (int)nudAxis_e_Postion.Value;     //获取E轴对应轴号的目标位置
                        if (dimension == 6) //如果插补维度==6
                        {
                            LineAllAxisArray[5] = (short)nudAxis_f.Value;      //获取F轴对应的轴号
                            LineAllPosition[5] = (int)nudAxis_f_Postion.Value; //获取F轴对应轴号的目标位置
                        }
                    }
                }
            }

            short ret = ecat_motion.M_Line_All(dimension, ref LineAllAxisArray[0], ref LineAllPosition[0], acc, vel, card);
            AddLog("开始插补运动", ret);

            Task task = new Task(() =>
            {
                for (int i = 0; i < dimension; i++)
                {
                    int Sts = 0;
                    do
                    {
                        Thread.Sleep(100);
                        ecat_motion.M_GetSts(LineAllAxisArray[i], out Sts, 1, card);//获取所有参与插补运动轴中的某个轴的轴状态
                    } while ((Sts & 0x400) == 0x400);//判断轴是否停止运动，sts的 bit 10 == 1则轴为运动状态，bit 10 == 0 则轴为静止状态
                }

                Action<int> action = (x) =>
                {
                    AddLog("插补运动完成");
                };
                lstAddLog.Invoke(action, 0);
            });
            task.Start(); 
        }

        private void btnStop_Click(object sender, EventArgs e)//停止（插补）运动
        {
            short ret = 0;
            int option = 0;//停止运动方式，0为平滑减速停止，1为急停减速
            foreach (var axis in LineAllAxisArray)
            {
                ret += ecat_motion.M_StopSingleAxis(axis, option, card);
            }
            AddLog("停止插补运动",ret);
        }
        #endregion

        #region 连续插补（M_CrdStart）
        private void btnCrd_1FIFO_0InitialContinueInterpolationMotion_Click(object sender, EventArgs e)//建立1#坐标系，初始化其0# FIFO
        {
            crdCfg_1.dimension = 3;         //插补维度
            crdCfg_1.axis = new short[8];   //为了数据对齐，该数组的长度必须为8，用户在使用时只需将参与连续插补的轴号放在crdCfg.axis[]的前4个元素里即可，后4个元素是无效的。 
        
            crdCfg_1.axis[0] = 1;           //第一个轴对应的轴号为轴1
            crdCfg_1.axis[1] = 2;           //第二个轴对应的轴号为轴2
            crdCfg_1.axis[2] = 3;            //第三个轴对应的轴号（因为前面crdCfg.dimension = 2，所以crdCfg.axis[2]和crdCfg.axis[3]无效，无需理会）
            //crdCfg_1.axis[3] = ;            //第四个轴对应的轴号（同理，因为前面crdCfg.dimension = 2，所以crdCfg.axis[2]和crdCfg.axis[3]无效，无需理会）

            crdCfg_1.orignPos = new int[8]; //同理，为了数据对齐，该数组的长度必须为8，用户在使用时只需将参与连续插补的轴号放在crdCfg.orignPos[]的前4个元素里即可，后面4个元素是无效的。
            for (int i = 0; i < crdCfg_1.dimension; i++)
            {
                crdCfg_1.orignPos[i] = 0;//起点坐标清零
            }

            crdCfg_1.evenTime = 10;           //最小匀速时间：使每段插补能够强制匀速运动的最小时间，若速度曲线只有加速度段和减速段，可以在尖峰处形成匀速的水平直线，减小速度突变带来的冲击，单位cycletime（默认 1ms）
            crdCfg_1.synVelMax = 100000;      //坐标系的最大限制插补速度：分段速度大于该速度会默认按该速度运动，单位 pulse/S
            crdCfg_1.synAccMax = 1000000;     //坐标系的最大限制插补加速度：分段加速度大于该加速度会默认按该加速度运动，单位 pulse/S/S                                                                                                      
            crdCfg_1.synDecSmooth = 1000000;  //插补平滑停止减速度：单位 pulse/S/S
            crdCfg_1.synDecAbrupt = 10000000; //插补紧急停止减速度：单位 pulse/S/S
            short ret = ecat_motion.M_SetCrd(crd_1, ref crdCfg_1, card); //设置连续插补运动参数
            AddLog("建立1#坐标系", ret);

            ret = ecat_motion.M_CrdClear(crd_1, 1, fifo_0, card);
            AddLog("清除1#坐标系的0# FIFO 中的位置缓存", ret);

            //检查是否开启“速度前瞻”功能
            if (chkCrd_1FIFO_0LookAhead.Checked == true)
            {
                ret = ecat_motion.M_SetVelPlanning(crd_1, fifo_0, (short)nudCrd_1FIFO_0LookAheadTime.Value, crdCfg_1.synAccMax, 100,ref crdBlockData_1, card);
                AddLog("1#坐标系开启前瞻功能", ret);
            }
        }

        private void btnCrd_1FIFO_0SetPointsToFifo_Click(object sender, EventArgs e)//向1#坐标系的0#FIFO中压入坐标点位
        {
            //压入直线插补 坐标A
            short[] axisArry1 = {1,2,3};//crd里的轴数组
            int[] posArray1 = { 10000, 20000, 30000 };//A点坐标
            short ret = ecat_motion.M_Line(crd_1, 3, ref axisArry1[0], ref posArray1[0], 10000, 100000, 0, fifo_0, card);
            AddLog("压入坐标A", ret);

            //压入直线插补 坐标B
            short[] axisArry2 = new short[] { 1,2,3 };//crd里的轴数组
            int[] posArray2 = new int[] { 20000, 10000, 0 };//B点坐标,也是2D圆弧起点
            ret = ecat_motion.M_Line(crd_1, 3, ref axisArry2[0], ref posArray2[0], 10000, 100000, 0, fifo_0, card);
            AddLog("压入坐标B", ret);

            ////压入do点,在“坐标B”处将do1 输出 1
            //ushort channel_1 = 1;
            //ushort doValue = 1;
            //ret = ecat_motion.M_BufIO(crd_1, channel_1, doValue, fifo_0, card);
            //AddLog("压入do_channel_1 == 1", ret);

            //压入以(半径基准)圆弧终点和半径压入的2D圆弧C
            //2D圆弧C的圆心绝对坐标（25000，10000）
            short[] axisArry4 = new short[] { 1, 2 };//crd里的轴数组
            int[] posArray3 = new int[] { 30000, 10000 };//圆弧C的终点
            double radius = 5000;                 //圆弧半径
            short circleDir1 = 1;                  //旋转方向 0：顺时针 1：逆时针
            ret = ecat_motion.M_Arc2R(crd_1, ref axisArry4[0], ref posArray3[0], radius, circleDir1, 10000, 100000, fifo_0, card);
            AddLog("M_Arc2R 压入圆弧C", ret);

            //压入以(圆心基准)圆弧终点和半径压入的2D圆弧C
            //2D圆弧C的圆心绝对坐标（25000，10000），但是不能直接传入
            //short[] axisArry3 = new short[] { 1, 2 };//crd里的轴数组
            //int[] posArray3 = new int[] { 30000, 10000 };//圆弧C的终点
            //double[] centerArray1 = { 0, 0 };//2D圆弧C的圆心绝对坐标（25000，10000）
            //short circleDir2 = 1;//旋转方向 0：顺时针 1：逆时针
            //centerArray1[0] = 25000 - posArray2[0];//指令里的圆心坐标参数为相对值，需要减一下
            //centerArray1[1] = 10000 - posArray2[1];//指令里的圆心坐标参数为相对值，需要减一下
            //ret = ecat_motion.M_Arc2C(crd_1,ref axisArry3[0],ref posArray3[0],ref centerArray1[0], circleDir2, 10000, 100000, fifo_0, card);
            //AddLog("M_Arc2C 压入圆弧C", ret);

            ////压入do点,在“坐标C”处将do1 输出 0
            //ushort channel_1 = 1;
            //ushort doValue = 0;
            //ret = ecat_motion.M_BufIO(crd_1, channel_1, doValue, fifo_0, card);
            //AddLog("压入do_channel_1 == 1", ret);

            //压入螺旋插补D
            int x = 25000;//螺旋插补D终点坐标X的值
            int y = 15000;//螺旋插补D终点坐标Y的值
            int z = 2500;//螺旋插补D终点坐标Z的值
            //圆心的绝对的坐标（25000，10000），但是不能直接传入
            double xCenter = 25000 - posArray3[0];//指令里的圆心坐标参数为相对值，需要减一下
            double yCenter = 10000 - posArray3[1];//指令里的圆心坐标参数为相对值，需要减一下
            short circleDir3 = 1;//旋转方向 0：顺时针 1：逆时针
            double pitch = 10000;//螺距
            ret = ecat_motion.M_HelicalLineXYC(crd_1, x, y, z, xCenter, yCenter, circleDir3, pitch, 10000, 100000, fifo_0, card);
            AddLog("压入螺旋插补D", ret);
        }

        private void btnCrd_1FIFO_0StartContinueInterpolationMotion_Click(object sender, EventArgs e)//1#坐标系(使用0# FIFO )开始执行连续插补运动
        {
            //检查1#坐标系是否开启“速度前瞻”功能
            if (chkCrd_1FIFO_0LookAhead.Checked == true)
            {
                short ret1 = lctdevice.ecat_motion.M_CrdData(crd_1, ref crdBlockData_1, fifo_0, card);
                AddLog("推送数据", ret1);
            }

            short ret2 = ecat_motion.M_CrdStartSingle(crd_1, fifo_0, card);//1#坐标系(使用0# FIFO )开始连续插补运动            
            AddLog("1#坐标系(使用0# FIFO )开始连续插补运动", ret2);

            Task task = new Task(() =>
            {
                do
                {
                    Thread.Sleep(10);
                    ret2 = ecat_motion.M_CrdStatus(crd_1, out Sts_1, out CmdNum_1, out Space_1, fifo_0, card);//获取1#坐标系的0#fifo的运行状态                    
                }
                while (Sts_1 == 1 || CmdNum_1 != 0);//判断插补运动状态和fifo_0里存在的点位个数
                Action<int> action1 = (x) =>
                {
                    AddLog("1#坐标系(使用0# FIFO )连续插补运动完成");
                };
                lstAddLog.Invoke(action1, 0);

                //关闭插补功能:连续插补运动完成后，需要要将连续插补的功能关闭
                Thread.Sleep(100);
                short mask_crd_1 = 1;//mask的bit0对应1#坐标系，bit1对应2#坐标系
                short option = 0;    //option 是对应的停止方法，0：平滑停止，1：急停
                ret2 = ecat_motion.M_CrdStop(mask_crd_1, option, card);//1#坐标系(使用0# FIFO )停止连续插补运动
                Action<int> action2 = (x) =>
                {
                    AddLog("关闭1#坐标系(使用0# FIFO )插补功能", ret2);
                };
                lstAddLog.Invoke(action2, ret2); 
            });
            task.Start();
        }

        private void btnCrd_1FIFO_0PauseContinueInterpolationMotion_Click(object sender, EventArgs e)//1#坐标系(使用0# FIFO )暂停插补运动
        {
            short ret = ecat_motion.M_CrdStop(crd_1, fifo_0, card);
            AddLog("1#坐标系(使用0# FIFO )暂停连续插补运动", ret);
        }

        private void btnCrd_1FIFO_0ContinueContinueInterpolationMotion_Click(object sender, EventArgs e)//1#坐标系(使用0# FIFO )继续插补运动
        {
            short ret = ecat_motion.M_CrdStart(crd_1, fifo_0, card); 
            AddLog("1#坐标系(使用0# FIFO )继续插补运动", ret); 
        }

        private void btnCrd_1FIFO_0StopContinueInterpolationMotion_Click(object sender, EventArgs e)//1#坐标系(使用0# FIFO )停止连续插补运动
        {
            short ret = ecat_motion.M_CrdStop(crd_1, fifo_0, card);

            while (true)
            {
                Thread.Sleep(10);
                ecat_motion.M_CrdStatus(crd_1, out Sts_1, out CmdNum_1, out Space_1, fifo_0, card);//获取fifo_0的运行状态
                if (Sts_1 != 1)                                                                    //运动状态【0：不在运动，1：正在运动】 
                    break;
            }

            ret += ecat_motion.M_CrdClear(crd_1, 1, fifo_0, card);
            AddLog("1#坐标系(使用0# FIFO )停止连续插补运动", ret);
        }

        private void btnCrd_2FIFO_0InitialContinueInterpolationMotion_Click(object sender, EventArgs e)//建立2#坐标系，初始化其0# FIFO
        {
            crdCfg_2.dimension = 6;         //插补维度
            crdCfg_2.axis = new short[8];   //为了数据对齐，该数组的长度必须为8，用户在使用时只需将参与连续插补的轴号放在crdCfg.axis[]的前4个元素里即可，后4个元素是无效的。 

            crdCfg_2.axis[0] = 1;            //第一个轴对应的轴号为轴1
            crdCfg_2.axis[1] = 2;            //第二个轴对应的轴号为轴2
            crdCfg_2.axis[2] = 3;            //第三个轴对应的轴号为轴3
            crdCfg_2.axis[3] = 4;            //第四个轴对应的轴号为轴4
            crdCfg_2.axis[4] = 5;            //第五个轴对应的轴号为轴5
            crdCfg_2.axis[5] = 6;            //第六个轴对应的轴号为轴6

            crdCfg_2.orignPos = new int[8]; //同理，为了数据对齐，该数组的长度必须为8，用户在使用时只需将参与连续插补的轴号放在crdCfg.orignPos[]的前4个元素里即可，后面4个元素是无效的。
            for (int i = 0; i < crdCfg_2.dimension; i++)
            {
                crdCfg_2.orignPos[i] = 0;//起点坐标清零
            }

            crdCfg_2.evenTime = 10;           //最小匀速时间：使每段插补能够强制匀速运动的最小时间，若速度曲线只有加速度段和减速段，可以在尖峰处形成匀速的水平直线，减小速度突变带来的冲击，单位cycletime（默认 1ms）
            crdCfg_2.synVelMax = 100000;      //坐标系的最大限制插补速度：分段速度大于该速度会默认按该速度运动，单位 pulse/S
            crdCfg_2.synAccMax = 1000000;     //坐标系的最大限制插补加速度：分段加速度大于该加速度会默认按该加速度运动，单位 pulse/S/S                                                                                                      
            crdCfg_2.synDecSmooth = 1000000;  //插补平滑停止减速度：单位 pulse/S/S
            crdCfg_2.synDecAbrupt = 10000000; //插补紧急停止减速度：单位 pulse/S/S
            short ret = ecat_motion.M_SetCrd(crd_2, ref crdCfg_2, card); //设置连续插补运动参数
            AddLog("建立2#坐标系", ret);

            ret = ecat_motion.M_CrdClear(crd_2, 1, fifo_0, card);
            AddLog("清除2#坐标系的0# FIFO 中的位置缓存", ret);

            //检查是否开启“速度前瞻”功能
            if (chkCrd_2FIFO_0LookAhead.Checked == true)
            {
                ret = ecat_motion.M_SetVelPlanning(crd_2, fifo_0, (short)nudCrd_2FIFO_0LookAheadTime.Value, crdCfg_2.synAccMax, 100, ref crdBlockData_2, card);
                AddLog("2#坐标系开启前瞻功能", ret);
            }
        }

        private void btnCrd_2FIFO_0SetPointsToFifo_Click(object sender, EventArgs e)//向2#坐标系的0#FIFO中压入坐标点位
        {
            //6轴直线插补
            //压入直线插补坐标 A1
            int[] posArray = { 10000, 10000, 10000, 10000, 10000, 10000 };//A1点坐标
            short ret = ecat_motion.M_Line(crd_2, crdCfg_2.dimension, ref crdCfg_2.axis[0], ref posArray[0], 10000, 100000, 0, fifo_0, card);
            AddLog("压入坐标A", ret);

            //压入直线插补坐标B
            posArray = new int[] { 20000, 10000, 20000, 10000, 30000, 20000 };//C点坐标
            ret = ecat_motion.M_Line(crd_2, crdCfg_2.dimension, ref crdCfg_2.axis[0], ref posArray[0], 10000, 100000, 0, fifo_0, card);
            AddLog("压入坐标B", ret);

            //压入直线插补坐标 C
            posArray = new int[] { -10000,-10000, -30000, -10000, 10000, -20000 };//C点坐标
            ret = ecat_motion.M_Line(crd_2, crdCfg_2.dimension, ref crdCfg_2.axis[0], ref posArray[0], 10000, 100000, 0, fifo_0, card);
            AddLog("压入坐标C", ret);

            //压入直线插补坐标 D
            posArray = new int[] { 50000, 10000, 10000, 50000, 40000, 30000 };//D点坐标
            ret = ecat_motion.M_Line(crd_2, crdCfg_2.dimension, ref crdCfg_2.axis[0], ref posArray[0], 10000, 100000, 0, fifo_0, card);
            AddLog("压入坐标D", ret);

            //压入直线插补坐标 E
            posArray = new int[] { 0, 0, 0, 0, 0, 0 };//E点坐标
            ret = ecat_motion.M_Line(crd_2, crdCfg_2.dimension, ref crdCfg_2.axis[0], ref posArray[0], 10000, 100000, 0, fifo_0, card);
            AddLog("压入坐标E", ret);
        }

        private void btnCrd_2FIFO_0StartContinueInterpolationMotion_Click(object sender, EventArgs e)//2#坐标系(使用0# FIFO )开始执行连续插补运动
        {
            //检查2#坐标系是否开启“速度前瞻”功能
            if (chkCrd_2FIFO_0LookAhead.Checked == true)
            {
                short ret1 = lctdevice.ecat_motion.M_CrdData(crd_2, ref crdBlockData_2, fifo_0, card);
                AddLog("推送数据", ret1);
            }

            short ret2 = ecat_motion.M_CrdStartSingle(crd_2, fifo_0, card);//2#坐标系(使用0# FIFO )开始连续插补运动            
            AddLog("1#坐标系(使用0# FIFO )开始连续插补运动", ret2);

            Task task = new Task(() =>
            {
                do
                {
                    Thread.Sleep(10);
                    ret2 = ecat_motion.M_CrdStatus(crd_2, out Sts_2, out CmdNum_2, out Space_2, fifo_0, card);//获取2#坐标系的0#fifo的运行状态                    
                }
                while (Sts_2 == 1 || CmdNum_2 != 0);//判断插补运动状态和fifo_0里存在的点位个数
                Action<int> action1 = (x) =>
                {
                    AddLog("2#坐标系(使用0# FIFO )连续插补运动完成");
                };
                lstAddLog.Invoke(action1, 0);

                //关闭插补功能:连续插补运动完成后，需要要将连续插补的功能关闭
                Thread.Sleep(100);
                short mask_crd_2 = 2;//mask的bit0对应1#坐标系，bit1对应2#坐标系
                short option = 0;    //option 是对应的停止方法，0：平滑停止，1：急停
                ret2 = ecat_motion.M_CrdStop(mask_crd_2, option, card);//2#坐标系(使用0# FIFO )停止连续插补运动 
                Action<int> action2 = (x) =>
                {
                    AddLog("关闭2#坐标系(使用0# FIFO )插补功能", ret2);
                };
                lstAddLog.Invoke(action2, ret2);
            });
            task.Start();
        }

        private void btnCrd_2FIFO_0PauseContinueInterpolationMotion_Click(object sender, EventArgs e)//2#坐标系(使用0# FIFO )暂停插补运动
        {
            short ret = ecat_motion.M_CrdStop(crd_2, fifo_0, card);
            AddLog("2#坐标系(使用0# FIFO )暂停连续插补运动", ret);
        }

        private void btnCrd_2FIFO_0ContinueContinueInterpolationMotion_Click(object sender, EventArgs e)//2#坐标系(使用0# FIFO )继续插补运动
        {
            short ret = ecat_motion.M_CrdStart(crd_2, fifo_0, card);
            AddLog("2#坐标系(使用0# FIFO )继续插补运动", ret); 
        }

        private void btnCrd_2FIFO_0StopContinueInterpolationMotion_Click(object sender, EventArgs e)//2#坐标系(使用0# FIFO )停止连续插补运动
        {
            short ret = ecat_motion.M_CrdStop(crd_2, fifo_0, card);

            while (true)
            {
                Thread.Sleep(10);
                ecat_motion.M_CrdStatus(crd_2, out Sts_2, out CmdNum_2, out Space_2, fifo_0, card);//获取fifo_0的运行状态
                if (Sts_2 != 1)                                                                    //运动状态【0：不在运动，1：正在运动】 
                    break;
            }

            ret += ecat_motion.M_CrdClear(crd_2, 1, fifo_0, card);
            AddLog("2#坐标系(使用0# FIFO )停止连续插补运动", ret);
        }
        #endregion

        private void mainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                for (short i = 1; i <= AxisNum; i++)
                    ecat_motion.M_Servo_Off(i, card);//所有轴去使能
                Thread.Sleep(100);
            }
            catch { }

            try
            {
                if (rdoConnectFastMode.Checked == true)//如果使用快速连接
                    ;
                else if(rdoConnectGeneralMode.Checked == true)//如果不使用快速连接
                    ecat_motion.M_DisconnectECAT(card);//断开总线
                Thread.Sleep(100);
            }
            catch { }

            try
            {
                ecat_motion.M_Close(card);//关闭M60轴卡
            }
            catch { }
        }

        #region 电子齿轮
        private void btnSetGear_Click(object sender, EventArgs e)//设置电子齿轮参数
        {
            short mastertype = 2;//主轴对象。1：主轴编码器反馈/2: 主轴规划位置/3：主轴/4：主轴规划输出值/5：主轴编码器输出值
            short masterItem = 0;//轴类型，mastertype为3时生效。0：主轴规划输出值/1：主轴编码器输出值
            short master = Convert.ToInt16(nudGearMasterAxis.Value);//主轴轴号
            short slaveA = Convert.ToInt16(nudGearSlaveAxisA.Value);//从轴A的轴号
            short slaveB = Convert.ToInt16(nudGearSlaveAxisB.Value);//从轴B的轴号
            int mastereven = Convert.ToInt32(nudMasterEven.Value);  //主轴的齿轮比
            int slaveevenA = Convert.ToInt32(nudSlaveEvenA.Value);  //从轴A的齿轮比
            int slaveevenB = Convert.ToInt32(nudSlaveEvenB.Value);  //从轴B的齿轮比

            int slaveApSts = 0;
            ecat_motion.M_GetSts(slaveA, out slaveApSts, 1, card);//获取从轴A的状态
            if ((slaveApSts & 0x400) != 0x400)//判断从轴A是否空闲，从轴A在运动状态下无法进行电子齿轮操作
            {
                short dir = 0;//跟随方向。0：双向跟随/1：正向跟随/-1：负向跟随
                short ret = ecat_motion.M_Gear(slaveA, dir, card);//将从轴切换到电子齿轮模式
                ret += ecat_motion.M_SetGearMaster(slaveA, master, mastertype, masterItem, card);//设置主从轴，以及从轴跟随对象
                int masterSlope = 0;//主轴离合区位移，范围>=0并且!=1
                ret += ecat_motion.M_SetGearRatio(slaveA, mastereven, slaveevenA, masterSlope, card);//设置电子齿轮比
                AddLog("开启轴" + slaveA + "电子齿轮," + "跟随轴" + master, ret);
            }
            else
            {
                MessageBox.Show("从轴" + slaveA + "正在运动，请检查各个轴状态");
            }

            int slaveBpSts = 0;
            ecat_motion.M_GetSts(slaveB, out slaveBpSts, 1, card);//获取从轴B的状态
            if ((slaveBpSts & 0x400) != 0x400)//判断从轴B是否空闲，从轴B在运动状态下无法进行电子齿轮操作
            {
                short dir = 0;//跟随方向。0：双向跟随/1：正向跟随/-1：负向跟随
                short ret = ecat_motion.M_Gear(slaveB, dir, card);//将从轴切换到电子齿轮模式
                ret += ecat_motion.M_SetGearMaster(slaveB, master, mastertype, 0, card);//设置主从轴，以及从轴跟随对象
                short masterSlope = 0;//主轴离合区位移，范围>=0并且!=1
                ret += ecat_motion.M_SetGearRatio(slaveB, mastereven, slaveevenB, masterSlope, card);
                AddLog("开启轴" + slaveB + "电子齿轮," + "跟随轴" + master, ret);
            }
            else
            {
                MessageBox.Show("从轴" + slaveB + "正在运动，请检查各个轴状态");
            }
        }

        private void btnGetGearParam_Click(object sender, EventArgs e)//获取电子齿轮参数
        {
            short masterindex,masterType,masterItem;
            int masterEven,slaveEven,masterSlope;

            short slaveA = Convert.ToInt16(nudGearSlaveAxisA.Value);//从轴A的轴号
            short ret = ecat_motion.M_GetGearMaster(slaveA, out masterindex, out masterType, out masterItem, card);
            ret += ecat_motion.M_GetGearRatio(slaveA, out masterEven, out slaveEven, out masterSlope, card);
            txtGetGearParamA.Text = "轴" + slaveA + "（从轴）绑定主轴" + masterindex + "（主轴）。齿轮比为：" + masterEven + "(主)/" + slaveEven+"(从轴)";

            short slaveB = Convert.ToInt16(nudGearSlaveAxisB.Value);//从轴B的轴号
            ret += ecat_motion.M_GetGearMaster(slaveB, out masterindex, out masterType, out masterItem, card);
            ret += ecat_motion.M_GetGearRatio(slaveB, out masterEven, out slaveEven, out masterSlope, card);
            AddLog("获取电子齿轮参数", ret);
            txtGetGearParamB.Text = "轴" + slaveB + "（从轴）绑定轴" + masterindex + "（主轴）。齿轮比为：" + masterEven + "(主)/" + slaveEven + "(从轴)";
        }

        private void btnStartGear_Click(object sender, EventArgs e)//从轴开启电子齿轮功能
        {
            short slaveA = Convert.ToInt16(nudGearSlaveAxisA.Value);//从轴A的轴号
            short ret = ecat_motion.M_GearStartSingleAxis(slaveA, card);
            AddLog("从轴" + slaveA + "开启电子齿轮功能", ret);
            short slaveB = Convert.ToInt16(nudGearSlaveAxisB.Value);//从轴B的轴号
            ret += ecat_motion.M_GearStartSingleAxis(slaveB, card);
            AddLog("从轴" + slaveB + "开启电子齿轮功能", ret);

            ////也可以使用“M_GearStart”方法，如下
            //short slaveA = Convert.ToInt16(nudGearSlaveAxisA.Value);//从轴A的轴号
            //short slaveB = Convert.ToInt16(nudGearSlaveAxisB.Value);//从轴B的轴号
            //uint mask = (uint)(Math.Pow(2, slaveA-1) + Math.Pow(2, slaveB-1));
            //short ret = ecat_motion.M_GearStart(mask, card);
            //AddLog("从轴" + slaveA + "和从轴" + slaveB + "开启电子齿轮功能", ret);
        }

        private void btnCancelGear_Click(object sender, EventArgs e)//从轴取消电子齿轮功能
        {
            short slaveA = Convert.ToInt16(nudGearSlaveAxisA.Value);//从轴A的轴号
            short ret = ecat_motion.M_StopSingleAxis(slaveA, card);
            AddLog("轴" + slaveA + "取消电子齿轮功能", ret);
            short slaveB = Convert.ToInt16(nudGearSlaveAxisB.Value);//从轴B的轴号
            ret = ecat_motion.M_StopSingleAxis(slaveB, card);
            AddLog("轴" + slaveB + "取消电子齿轮功能", ret);

            ////也可以使用“M_Stop”方法，如下
            //uint option = 0;//option为停止运动的方式: 0 表示采用平滑减速度停止，1 表示采用急停减速度停止
            //short slaveA = Convert.ToInt16(nudGearSlaveAxisA.Value);//从轴A的轴号
            //short slaveB = Convert.ToInt16(nudGearSlaveAxisB.Value);//从轴B的轴号
            //uint mask = (uint)(Math.Pow(2, slaveA - 1) + Math.Pow(2, slaveB - 1));
            //short ret = ecat_motion.M_Stop(mask, option, card);
            //AddLog("从轴" + slaveA + "和从轴" + slaveB + "取消电子齿轮功能", ret);
        }
        #endregion

        private void btnSetCurrentAxisPostion_Click(object sender, EventArgs e)//设置当前轴的位置坐标设置为某个数值
        {
            short ret = ecat_motion.M_SetCurrentPos(currentAxisID, (int)nudSetCurrentAxisPostion.Value, card);
            AddLog("设置轴" + currentAxisID + "的当前位置为：" + (int)nudSetCurrentAxisPostion.Value, ret);
        }

        #region 数字量IO
        private void btnM_Get_Digital_Chn_Input_Click(object sender, EventArgs e)//读取从站中第某个DI通道的数值
        {
            short channel = (short)nudM_Get_Digital_Chn_Input_Channel.Value;
            short value = 0;  
            short ret = ecat_motion.M_Get_Digital_Chn_Input(channel,out value,card);
            if (ret == 0)
                txtM_Get_Digital_Chn_Input.Text = value.ToString();
            AddLog("读取从站中第" + channel + "个DI通道的数值", ret);        
        }

        private void btnM_Get_Slave_Digital_Chn_Input_Click(object sender, EventArgs e)//读取第某个io模块的第某个DI通道的数值
        {
            short ioM = (short)nudM_Get_Slave_Digital_Chn_Input_Slave.Value;
            short channel = (short)nudM_Get_Slave_Digital_Chn_Input_Channel.Value;
            short value = 0; 
            short ret = ecat_motion.M_Get_Slave_Digital_Chn_Input(ioM, channel, ref value,card);
            if (ret == 0)
                txtM_Get_Slave_Digital_Chn_Input.Text = value.ToString();
            AddLog("读取第" + ioM + "个io模块的第" + channel + "个DI通道的数值", ret);
        }

        private void btnM_Get_Digital_Port_Input_Click(object sender, EventArgs e)//读取从站中从第某个DI通道开始往后共32个DI通道的数值(通道号不足就补0)
        {
            short beginChannel = (short)nudM_Get_Digital_Port_Input_BeginChannel.Value;
            uint value = 0;
            short ret = ecat_motion.M_Get_Digital_Port_Input(beginChannel, ref value, card);
            if (ret == 0)
                txtM_Get_Digital_Port_Input.Text = value.ToString();
            AddLog("读取从站中从第" + beginChannel + "个DI通道开始往后共32个DI通道的数值", ret);
        }

        private void btnM_Get_Slave_Digital_Port_Input_Click(object sender, EventArgs e)//读取第某个io模块从其第某个DI通道开始往后共32个DI通道的数值(通道号不足就补0)
        {
            short ioM = (short)nudM_Get_Slave_Digital_Port_Input_Slave.Value;
            short beginChannel = (short)nudM_Get_Slave_Digital_Port_Input_BeginChannel.Value;
            uint value = 0;
            short ret = ecat_motion.M_Get_Slave_Digital_Port_Input(ioM,beginChannel, ref value, card);
            if (ret == 0)
                txtM_Get_Slave_Digital_Port_Input.Text = value.ToString();
            AddLog("读取第" + ioM + "个io模块从其第" + beginChannel + "个DI通道开始往后共32个DI通道的数值", ret);
        }

        private void btnM_Set_Digital_Chn_Output_Click(object sender, EventArgs e)//设置从站中第某个DO通道的数值,并读取其输出状态
        {
            short channel = (short)nudM_Set_Digital_Chn_Output_Channel.Value;
            short value = (short)nudM_Set_Digital_Chn_Output_ChannelValue.Value;
            short ret = ecat_motion.M_Set_Digital_Chn_Output(channel, value,card);
            AddLog("设置从站中第" + channel + "个DO通道的数值为" + value, ret);

            ret += ecat_motion.M_Get_Digital_Chn_Output(channel, out value, card);
            if (ret == 0)
                txtM_Get_Digital_Chn_Output_ChannelActualValue.Text = value.ToString();
            AddLog("读取从站中第" + channel + "个DO通道的数值", ret);
        }

        private void btnM_Set_Slave_Digital_Chn_Output_Click(object sender, EventArgs e)//设置第某个io模块的第某个DO通道的数值，并读取其输出状态
        {
            short ioM = (short)nudM_Set_Slave_Digital_Chn_Output_Slave.Value;
            short channel = (short)nudM_Set_Slave_Digital_Chn_Output_Channel.Value;
            short value = (short)nudM_Set_Slave_Digital_Chn_Output_Value.Value;
            short ret = ecat_motion.M_Set_Slave_Digital_Chn_Output(ioM, channel, value,card);
            AddLog("设置第" + ioM + "个io模块的第" + channel + "个DO通道的数值为" + value, ret);

            ret += ecat_motion.M_Get_Slave_Digital_Chn_Output(ioM, channel, ref value,card);
            if (ret == 0)
                txtM_Get_Slave_Digital_Chn_Output_ChannelActualValue.Text = value.ToString();
            AddLog("读取第" + ioM + "个io模块的第" + channel + "个DO通道的数值", ret);
        }

        private void btnM_Set_Digital_Port_Output_Click(object sender, EventArgs e)//设置从站中从第某个DO通道开始往后共32个DO通道的数值，并读取其输出状态
        {
            short beginChannel = (short)nudM_Set_Digital_Port_Output_BeginChannel.Value;
            uint value = (uint)nudM_Set_Digital_Port_Output_Value.Value;
            short ret = ecat_motion.M_Set_Digital_Port_Output(beginChannel, value, uint.MaxValue, card);
            AddLog("设置从站中从第" + beginChannel + "个DO通道开始往后共32个DO通道的数值为" + value, ret);

            ret += ecat_motion.M_Get_Digital_Port_Output(beginChannel,ref value,card);
            if (ret == 0)
                txtM_Get_Digital_Port_Output_ChannelActualValue.Text = value.ToString();
            AddLog("读取从站中从第" + beginChannel + "个DO通道开始往后共32个DO通道的数值", ret);
        }

        private void btnM_Set_Slave_Digital_Port_Output_Click(object sender, EventArgs e)//设置第某个io模块从其第某个DO通道开始往后共32个DO通道的数值，并读取其输出状态
        {
            short ioM = (short)nudM_Set_Slave_Digital_Port_Output_Slave.Value;
            short beginChannel = (short)nudM_Set_Slave_Digital_Port_Output_BeginChannel.Value;
            uint value = (uint)nudM_Set_Slave_Digital_Port_Output_Value.Value;
            short ret = ecat_motion.M_Set_Slave_Digital_Port_Output(ioM, beginChannel, value, UInt32.MaxValue, card);
            AddLog("设置第" + ioM + "个io模块从其第" + beginChannel + "个DO通道开始往后共32个DO通道的数值为：" + value, ret);

            ret += ecat_motion.M_Get_Slave_Digital_Port_Output(ioM, beginChannel, ref value,card);
            if (ret == 0)
                txtM_Get_Slave_Digital_Port_Output_ChannelActualValue.Text = value.ToString();
            AddLog("读取第" + ioM + "个io模块从其第" + beginChannel + "个DO通道开始往后共32个DO通道的数值", ret);
        }
        #endregion

        #region 模拟量IO
        private void btnM_Get_Analog_Input_Click(object sender, EventArgs e)//读取从站中从第某个AI通道开始往后共2路模拟量输入通道的数值
        {
            short beginChannel = (short)nudM_Get_Analog_Input_BeginChannel.Value;
            short count = 2;//这里以一次性读取2通道模拟量输入举例
            short[] value = new short[2];//所以该数组的长度为2
            short ret = ecat_motion.M_Get_Analog_Input(beginChannel, out value[0], count,card);
            if (ret == 0)
                txtM_Get_Analog_Input.Text = value[0] + "和" + value[1];
            AddLog("读取从站中从第" + beginChannel + "个AI通道开始往后共2路模拟量输入通道的数值", ret);
        }

        private void btnM_Get_Slave_Analog_Input_Click(object sender, EventArgs e)
        {
            short ioM = (short)nudM_Get_Slave_Analog_Input_Slave.Value;
            short beginChannel = (short)nudM_Get_Slave_Analog_Input_BeginChannel.Value;
            short count = 2;//这里以一次性读取2通道模拟量输入举例
            short[] value = new short[count];//所以该数组的长度为2
            short ret = ecat_motion.M_Get_Slave_Analog_Input(ioM, beginChannel,ref value[0], count,card);
            if (ret == 0)
                txtM_Get_Slave_Analog_Input.Text = value[0] + "和" + value[1];
            AddLog("读取第" + ioM + "模块从其第" + beginChannel + "个AI通道开始往后共2路模拟量输入通道的数值", ret);
        }

        private void btnM_Set_Analog_Output_Click(object sender, EventArgs e)//设置从站中从第某个AO通道开始往后共2路模拟量输出通道的数值，并读取其输出数值
        {
            short beginChannel = (short)nudM_Set_Analog_Output_BeginChannel.Value;
            short count = 2;//这里以一次性设置2路模拟量输出通道举例
            short[] value = new short[count];//所以该数组的长度为2
            value[0] = (short)nudM_Set_Analog_Output_Value1.Value;
            value[1] = (short)nudM_Set_Analog_Output_Value2.Value;
            short ret = ecat_motion.M_Set_Analog_Output(beginChannel, ref value[0], count,card);
            AddLog("设置从站中从第" + beginChannel + "个AO通道开始往后共2路模拟量输出通道的数值", ret);

            ret += ecat_motion.M_Get_Analog_Output(beginChannel, out value[0], count, card);
            if (ret == 0)
                txtM_Get_Analog_Output.Text = value[0] + "和" + value[1];
            AddLog("读取从站中从第" + beginChannel + "个AO通道开始往后共2路模拟量通道的输出数值", ret);
        }

        private void btnM_Set_Slave_Analog_Output_Click(object sender, EventArgs e)//设置第某个io模块从其第某个AO通道开始往后共2路模拟量输出通道的数值，并读取其输出数值
        {
            short ioM = (short)nudM_Set_Slave_Analog_Output_Slave.Value;
            short beginChannel = (short)nudM_Set_Slave_Analog_Output_BeginChannel.Value;
            short count = 2;//这里以一次性设置2路模拟量输出通道举例
            short[] value = new short[count];//所以该数组的长度为2
            value[0] = (short)nudM_Set_Slave_Analog_Output_Value1.Value;
            value[1] = (short)nudM_Set_Slave_Analog_Output_Value2.Value;
            short ret = ecat_motion.M_Set_Slave_Analog_Output(ioM, beginChannel, ref value[0], count,card);
            AddLog("设置第" + ioM + "个io模块从其第" + beginChannel + "个AO通道开始往后共2路模拟量输出通道的数值", ret);

            ret += ecat_motion.M_Get_Slave_Analog_Output(ioM, beginChannel, ref value[0], count, card);
            if (ret == 0)
                txtM_Get_Slave_Analog_Output.Text = value[0] + "和" + value[1];
            AddLog("读取第" + ioM + "个io模块从其第" + beginChannel + "个AO通道开始往后共2路模拟量输出通道的数值", ret);
        }
       
        short DigitalData = 0;
        double DigitalData_temp = 0;
        private double LoadCellCalculation(short DigitalData, int Range, double Accuracy)//压力值转换公式
        {
            double PressureValue = (double)(DigitalData - DigitalData_temp) / (int)(32767 * (Accuracy / 2)) * (double)Range;
            return PressureValue;
        }

        private void btnClear_Click(object sender, EventArgs e)//数值清零（抵消漂移）
        {
            if (btnClear.Text == "数值清零")
            {
                DigitalData_temp = DigitalData;
                btnClear.Text = "数值还原";
            }
            else
            {
                DigitalData_temp = 0;
                btnClear.Text = "数值清零";
            }
        }

        private void btnReadPressureValue_Click(object sender, EventArgs e)//读取压力值
        {
            short LC3162LoadCellChannel = (short)nudLC3162LoadCellChannel.Value;
            short count = 1;//这里以一次只读取1通道模拟量输入举例
            short value = 0;
            short ret = ecat_motion.M_Get_Analog_Input(LC3162LoadCellChannel, out value, count,card);
            if (ret == 0)
            {
                DigitalData = value;
                double LoadCellAccuracy = (double)nudLoadCellAccuracy.Value;//压力传感器的精度
                int LoadCellRange = (int)nudLoadCellRange.Value;//压力传感器的量程
                double Pressure = LoadCellCalculation(DigitalData, LoadCellRange, LoadCellAccuracy);//压力值转换公式
                txtPressureValue.Text = Pressure.ToString();
            }                                            
        }
        #endregion

        private void btnSdoReadObjectValue_Click(object sender, EventArgs e)//SDO读操作
        {
            short slave = decimal.ToInt16(nudSdoReadSlaveID.Value);
            string index_ = "0x" + txtSdoReadIndex.Text;
            short index = Convert.ToInt16(index_, 16);
            short subindex = decimal.ToInt16(nudSdoReadSubIndex.Value);
            short data_size = decimal.ToInt16(nudSdoReadDataSize.Value);
            short count = 1;//一次只读取某个索引的1个子索引的值
            uint Buf = 0;
            short ret = ecat_motion.M_EcatSDORead(slave, index, subindex, data_size, out Buf, count, card);
            if (ret == 0)
                txtObjectValue.Text = Buf.ToString();
            AddLog("SDO读操作", ret);
        }

        private void btnWriteValueToObject_Click(object sender, EventArgs e)//SDO写操作
        {
            short slave = decimal.ToInt16(nudSdoWriteSlaveID.Value);
            string index_ = "0x" + txtSdoWriteIndex.Text;
            short index = Convert.ToInt16(index_, 16);
            short subindex = decimal.ToInt16(nudSdoWriteSubIndex.Value);
            uint data = decimal.ToUInt32(nudSdoWriteData.Value);
            short data_size = decimal.ToInt16(nudSdoWriteDataSize.Value);
            short ret = ecat_motion.M_EcatSDOWrite(slave, index, subindex, data, data_size, card);
            AddLog("SDO写操作", ret);
        }

        private void btnGetOperationMode_Click(object sender, EventArgs e)
        {
            short mode = 0;
            short ret = ecat_motion.M_EcatGetOperationMode(currentAxisID, ref mode, card);//获取驱动器的工作模式
            if (ret == 0)
            {
                AddLog("M_EcatGetOperationMode执行", ret);
                if (mode == 6)
                {
                    txtGetOperationMode.Text = "回零模式";
                }
                else if (mode == 8)
                {
                    txtGetOperationMode.Text = "同步位置模式";
                }
                else
                {
                    txtGetOperationMode.Text = "其他";
                }
            }
            else
            {
                AddLog("M_EcatGetOperationMode执行", ret);
            }
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked  == true)
                btnSetCurrentAxisHomeParameters.Enabled = true;
            else
                btnSetCurrentAxisHomeParameters.Enabled = false;
        }

        private void btnSetModeTo10_Click(object sender, EventArgs e)
        {
            //10:模式10为EtherCAT型驱动器的转矩模式
            short ret = ecat_motion.M_EcatSetOperationMode(currentAxisID, 10, card);
            if (ret != 0)
            {
                MessageBox.Show("M_EcatSetOperationMode ret == " + ret);
                return;
            }
        }

        private void btnWriteTargetTorque_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_WriteTargetTorque(currentAxisID, (short)nudTargetTorque.Value,(double)nud_TorqueVel.Value, card);
            if (ret != 0)
            {
                MessageBox.Show("M_WriteTargetTorque ret == " + ret);
            }
        }

        private void btnReadActualTorque_Click(object sender, EventArgs e)
        {
            short pTorque = 0;
            short ret = ecat_motion.M_ReadActualTorque(currentAxisID, ref pTorque,1, card);
            if (ret == 0)
                txtReadActualTorque.Text = pTorque.ToString() + "%";
            else
                MessageBox.Show("M_ReadActualTorque ret == " + ret);
        }

        private void btnSetModeTo8_Click(object sender, EventArgs e)
        {
            //8:模式8为EtherCAT型驱动器的位置模式
            short ret = ecat_motion.M_EcatSetOperationMode(currentAxisID, 8, card);
            if (ret != 0)
            {
                MessageBox.Show("M_EcatSetOperationMode ret == " + ret);
                return;
            }
        }

        private void 开启AxisStopDi_Click(object sender, EventArgs e)
        {
            short enable =1;  //急停DI 启动状态，0-不启动 1-启动
            short card = 0;    //轴卡卡号,从0开始
            short ret = ecat_motion.M_SetAxisStopDi(currentAxisID, enable, (short)(DiType.Value), (short)(index.Value), (short)(trigSrc.Value), (short)(stopType.Value), card);
                if (ret != 0)
            {
                MessageBox.Show("M_SetAxisStopDi Error!");
            }
        }

        private void 关闭AxisStopDi_Click(object sender, EventArgs e)
        {
            short enable = 0;  //急停DI 启动状态，0-不启动 1-启动
            short card = 0;    //轴卡卡号,从0开始
            short ret = ecat_motion.M_SetAxisStopDi(currentAxisID, enable, (short)(DiType.Value), (short)(index.Value), (short)(trigSrc.Value), (short)(stopType.Value), card);
            if (ret != 0)
            {
                MessageBox.Show("M_SetAxisStopDi Error!");
            }
        }

        private void btnSet607FValue_Click(object sender, EventArgs e)
        {
            short slaveNo = 0;
            short ret = ecat_motion.M_GetAxisSlaveIndex(currentAxisID, ref slaveNo, card);
            AddLog("M_GetAxisSlaveIndex", ret);
            uint data = (uint)nud607FValue.Value;
            ret = ecat_motion.M_EcatSDOWrite(slaveNo, 0x607F, 0x00, data, 4, card);
            AddLog("设置扭矩模式下的最大限速", ret);
        }

        private void btnSetTorqueOffset_Click(object sender, EventArgs e)
        {
            short slaveNo = 0;
            short ret = ecat_motion.M_GetAxisSlaveIndex(currentAxisID,ref slaveNo,card);
            AddLog("M_GetAxisSlaveIndex", ret);
            uint data = (uint)nud60B2.Value;
            ret = ecat_motion.M_EcatSDOWrite(slaveNo, 0x60B2, 0x00, data, 2, card);
            AddLog("设置扭矩模式下的最大限速", ret);
        }

        /// <summary>
        /// 回零1 回零成功切换位置模式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartHoming_1_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_SetHomingMode(currentAxisID, 6, card);//设置驱动器的工作模式，6对应驱动器的回零工作模式(Homing)
            Thread.Sleep(50);//此延时有必要
            if (ret == 0)
            {
                ret = ecat_motion.M_HomingStart(currentAxisID, card);//开始回零运动
                if (ret == 0)
                {
                    Task task = new Task(() =>
                    {
                        int currentAxisStatus = 0;
                        do
                        {
                            Thread.Sleep(100);
                            ecat_motion.M_GetSts(currentAxisID, out currentAxisStatus, 1, card);//获取轴的状态
                        }
                        while ((currentAxisStatus & 0x400) == 0x400);//轴停止运动后跳出循环
                        short mode = 0;
                        short count = 0;
                        do
                        {
                            ecat_motion.M_SetHomingMode(currentAxisID, 8, card);
                            count++;
                            Thread.Sleep(50);
                            ecat_motion.M_EcatGetOperationMode(currentAxisID, ref mode, card);
                            if ((mode == 8)||(count>10))
                                break;
                        } while (true);
                        ecat_motion.M_ClrSts(currentAxisID, 1, 0);

                        if (count <= 10)
                        {
                            double Cmd = 0;
                            short Ret = ecat_motion.M_GetCmd(currentAxisID, out Cmd, 1, card);//获取轴规划位置
                            if (Ret == 0)
                            {
                                short HomeMode = 0;      //回零模式
                                int HomeOffSet = 0;      //回零偏移
                                uint HomeSpeed1 = 0;     //第一回零速度
                                uint HomeSpeed2 = 0;     //第二回零速度
                                uint HomeAcc = 0;        //回零加速度
                                ushort ProbeFunction = 0;//预留，默认为 0
                                ret = ecat_motion.M_GetHomingPrm(currentAxisID, ref HomeMode, ref HomeOffSet, ref HomeSpeed1, ref HomeSpeed2, ref HomeAcc, ref ProbeFunction, card);
                                if (ret == 0)
                                {
                                    if (((currentAxisStatus & 0x10000) != 0x10000) && ((currentAxisStatus & 0x400) != 0x400) && (Math.Abs(Cmd - HomeOffSet) < 50))//50表示50个脉冲，经验值。如果(轴没有给出HomingError信号)&&(轴不再运动)&&(当前位置在0附近)
                                    {
                                        Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "回零_成功"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; }; lstAddLog.Invoke(action);
                                        MessageBox.Show("轴" + currentAxisID + "回零_成功");
                                    }
                                    else
                                    {
                                        Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "回零异常"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; }; lstAddLog.Invoke(action);
                                        MessageBox.Show("轴" + currentAxisID + "回零异常");
                                    }
                                }
                                else
                                {
                                    MessageBox.Show("轴" + currentAxisID + "获取回零参数_失败！\n回零异常");
                                }
                            }
                            else
                            {
                                Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "回零异常"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; }; lstAddLog.Invoke(action);
                                MessageBox.Show("ecat_motion.M_GetCmd ret == "+ Ret + "\n轴" + currentAxisID + "回零异常");
                            }
                        }
                        else
                        {
                            MessageBox.Show("轴" + currentAxisID + "设置_位置模式_失败！\n回零异常");
                        }
                    });
                    task.Start();
                }
                else
                {
                    MessageBox.Show("轴" + currentAxisID + "开始回零_失败");
                }
            }
            else
            {
                MessageBox.Show("将轴" + currentAxisID + "设置为回零模式_失败");
            }
        }

        /// <summary>
        /// 回零 简化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStartHoming_3_Click(object sender, EventArgs e)
        {
            short ret = ecat_motion.M_SetHomingMode(currentAxisID, 6, card);//设置驱动器的工作模式，6对应驱动器的回零工作模式(Homing)
            Thread.Sleep(50);//此延时有必要
            if (ret == 0)
            {
                ret = ecat_motion.M_HomingStart(currentAxisID, card);//开始回零运动
                if (ret == 0)
                {
                    Task task = new Task(() =>
                    {
                        int Sts = 0;
                        do
                        {
                            Thread.Sleep(100);
                            ecat_motion.M_GetSts(currentAxisID, out Sts, 1, card);
                            if ((Sts & 0x400) != 0x400)//如果轴不再运动了（Move信号是bit10）
                                break;
                        } while (true);

                        double Cmd = 0;
                        short Ret = ecat_motion.M_GetCmd(currentAxisID, out Cmd, 1, card);//获取轴规划位置
                        if (Ret == 0)
                        {
                            short HomeMode = 0;      //回零模式
                            int HomeOffSet = 0;      //回零偏移
                            uint HomeSpeed1 = 0;     //第一回零速度
                            uint HomeSpeed2 = 0;     //第二回零速度
                            uint HomeAcc = 0;        //回零加速度
                            ushort ProbeFunction = 0;//预留，默认为 0
                            ret = ecat_motion.M_GetHomingPrm(currentAxisID, ref HomeMode, ref HomeOffSet, ref HomeSpeed1, ref HomeSpeed2, ref HomeAcc, ref ProbeFunction, card);
                            if (ret == 0)
                            {
                                if (((Sts & 0x10000) != 0x10000) && (Math.Abs(Cmd - HomeOffSet) < 50))//50表示50个脉冲，经验值。如果(轴没有给出HomingError信号)&&(当前位置在0附近)
                                {
                                    Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "回零_成功"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; }; lstAddLog.Invoke(action);
                                    MessageBox.Show("轴" + currentAxisID + "回零_成功");
                                }
                                else
                                {
                                    Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "回零异常"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; }; lstAddLog.Invoke(action);
                                    MessageBox.Show("轴" + currentAxisID + "回零异常");
                                }
                            }
                            else
                            {
                                MessageBox.Show("轴" + currentAxisID + "设置_位置模式_失败！\n回零异常");
                            }
                        }
                        else
                        {
                            Action action = () => { lstAddLog.Items.Add("轴" + currentAxisID + "回零异常"); lstAddLog.SelectedIndex = lstAddLog.Items.Count - 1; }; lstAddLog.Invoke(action);
                            MessageBox.Show("ecat_motion.M_GetCmd ret == " + Ret + "\n轴" + currentAxisID + "回零异常");
                        }
                    });
                    task.Start();
                }
                else
                {
                    MessageBox.Show("轴" + currentAxisID + "开始回零_失败");
                }
            }
            else
            {
                MessageBox.Show("将轴" + currentAxisID + "设置为回零模式_失败");
            }
        }

        private void mainForm_Load(object sender, EventArgs e)
        {
            card = (short)nudCurrentCardId.Value;
        }

        short dataNum = 0;
        private void btn_get_digital_input_ex_Click(object sender, EventArgs e)
        {
            DI_lstLog.Items.Clear();
            short chn = 0;
            short dataNum = 0;
            IntPtr dataPtr = IntPtr.Zero;//数据指针--数据大小
            short ret = ecat_motion.M_Get_Digital_Input_Ex(ref chn, ref dataNum, ref dataPtr, 0);
            short[] diData = new short[dataNum];
            if (dataNum > 0 && dataPtr != IntPtr.Zero)
            {
                Marshal.Copy(dataPtr, diData, 0, dataNum);
                //show DI_data  
                for (int i = 0; i < dataNum; i++)
                {
                    AddLog1(diData[i]);
                }
                AddLog("读取DI成功！");
            }
        }

        private void btn_get_digital_output_ex_Click(object sender, EventArgs e)
        {
            DO_lstLog.Items.Clear();
            short chn = 0;//通道号-chn
            //short chnNum = 0;
            short dataNum = 0;//数据个数
            IntPtr dataPtr = IntPtr.Zero;//数据指针--数据大小
            short ret = ecat_motion.M_Get_Digital_Output_Ex(ref chn, ref dataNum, ref dataPtr, 0);
            short[] diData = new short[dataNum];
            if (dataNum > 0 && dataPtr != IntPtr.Zero)
            {
                Marshal.Copy(dataPtr, diData, 0, dataNum);
                //show DI_data
                for (int i = 0; i < dataNum; i++)
                {
                    AddLog2(diData[i]);
                }
                AddLog("读取DO成功！");
            }
        }
        uint cnt = 0;
        /// <summary>
        /// crc 校验
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_CRC_Click(object sender, EventArgs e)
        {
            short ret = 0;
            int[] err = new int[64];//有多少从站，数组大小建多大
            short datanum = 0;//数据个数
            ret = ecat_motion.M_GetCrcErrorInfoEx(ref err[0], ref datanum, 0);
            ;
            if (ret == 0)
            {
                if (cnt == 0)
                {
                    crc_box1.Items.Clear();
                    for (int i = 0; i < datanum; i++)
                    {
                        //crc_box1.
                        cnt = 1;
                        crcLog1("从站" + (i + 1) + ":", err[i]);
                    }
                    AddLog("读取crc信息成功！");
                }
                else
                {
                    crc_box2.Items.Clear();
                    for (int i = 0; i < datanum; i++)
                    {
                        //crc_box1.
                        cnt = 0;
                        crcLog2("从站" + (i + 1) + ":", err[i]);
                    }
                    AddLog("读取crc信息成功！");
                }
                
            }
            else
            {
                AddLog("读取crc信息失败！",ret);
            }
        }
    }
}
